/* -*- Mode: C++; indent-tabs-mode: nil; tab-width: 4 -*-
 * -*- coding: utf-8 -*-
 *
 * Copyright (C) 2022 KylinSoft Co., Ltd.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "proxy-service-manager.h"


ProxyServiceManager::ProxyServiceManager(QObject *parent) : QObject(parent)
{
    qDBusRegisterMetaType<QStringList>();
    qDBusRegisterMetaType<QMap<QString, QStringList >>();
    QDBusConnection sessionBus = QDBusConnection::sessionBus();
    if (sessionBus.registerService("org.ukui.SettingsDaemon")) {
        sessionBus.registerObject("/org/ukui/SettingsDaemon/AppProxy",this,
                                  QDBusConnection::ExportAllContents);
    }
}

void ProxyServiceManager::init()
{
    m_time->stop();
    m_proxyInterface = new QDBusInterface("com.kylin.system.proxy",
                                          "/com/kylin/system/proxy/App",
                                          "com.kylin.system.proxy.App",
                                          QDBusConnection::systemBus());

    connect(KWindowSystem::self(), &KWindowSystem::windowAdded, this, &ProxyServiceManager::onWindowAdded);

    initProxyState();
}

void ProxyServiceManager::initProxyState()
{
    QString jsonPath = QDir::homePath() + "/" + PROXYCONF_FILE;
    QJsonObject readObj = readJsonFile(jsonPath);
    if (!readObj.value(PROTOJSON_KEY_TYPE).toString().isNull()
        && !readObj.value(PROTOJSON_KEY_SERVER).toString().isNull()
        && !readObj.value(PROTOJSON_KEY_PORT).isNull()
        && readObj.value(PROTOJSON_KEY_STATE).toBool()) {
        startProxy(readObj);
        qDebug()<<Q_FUNC_INFO<<__LINE__<<"-------------- On";
    } else {
        stopProxy();
        qDebug()<<Q_FUNC_INFO<<__LINE__<<"-------------- Stop";
    }
}

void ProxyServiceManager::start()
{
    qDebug()<<"ProxyServiceManager ---------------start";

    m_time = new QTimer(this);
    connect(m_time,SIGNAL(timeout()),this,SLOT(init()));
    m_time->start(10);
}


void ProxyServiceManager::stop()
{
    qDebug()<<"ProxyServiceManager ---------------stop";
}

void ProxyServiceManager::datacpy(void *dest,int dstLen, const void *src, int srcLen)
{
    if (srcLen == 0 || dest == NULL || src == NULL || dstLen == 0) {
        printf("error:source buffer or dest buffer is empty!\n");
        return;
    }

    int nCount = srcLen > dstLen?dstLen:srcLen;

    char *pDest = (char *)dest;
    char *pSrc = (char *)src;
    for(int i = 0; i < nCount; i++) {
        *pDest++ = *pSrc++;
    }
    return;
}


QString ProxyServiceManager::getTerminalOutput(const QString & strCmd)
{
    QString strInfo;
    strInfo.clear();
    const int nMaxlen = 1024 * 500;
    FILE *fstream = NULL;
    char buff[nMaxlen] = { '\0' };
    char temp[1024] = { '\0' };
    int len = 0;
    int offset = 0;
    memset(buff, '\0', sizeof(buff));
    memset(temp, '\0', sizeof(temp));

    // 通过管道来回去系统命令返回的值
    if (NULL == (fstream = popen(strCmd.toUtf8().data(), "r"))) {
        return strInfo;
    }

    while (NULL != fgets(temp, sizeof(temp), fstream)) {
        if (temp[0] == '\0') {
            continue;
        }
        len = strlen(temp);
        if ((offset + len) > nMaxlen) {
            break;
        }

        datacpy(buff + offset, sizeof(buff)-offset, temp, len);
        offset += len;
    }
    buff[sizeof(buff) - 1] = '\0';
    strInfo = buff;

    pclose(fstream);

    return strInfo;
}

QStringList ProxyServiceManager::getProcAllPid(const QString& ppid)
{
    QStringList pid_all;
    pid_all.clear();
    if (ppid.isEmpty()) {
        return pid_all;
    }


    QString strCmd = "pgrep -P " + ppid;
    QString strInfo = getTerminalOutput(strCmd);
    if (!strInfo.isEmpty()) {
        QStringList pidList;
        pid_all.append(ppid);
        pidList = strInfo.split('\n');
        for (int i = 0; i < pidList.size(); i ++) {
            QString pidItem = pidList.at(i);
            if (pidItem.isEmpty()) {
                continue;
            }
            qDebug()<<Q_FUNC_INFO<<__LINE__<<"----"<<pidItem;
            pid_all.append(getProcAllPid(pidItem));
        }
    } else {
        qDebug() << Q_FUNC_INFO << __LINE__ << "get " << strCmd << "is empty";
        pid_all.append(ppid);
    }
    return pid_all;
}

bool ProxyServiceManager::getPidByName()
{
    QString strCmd = QString("ps -ef | grep tdappdesktop | awk '{printf $2;printf\"\\n\";}'");
    QString strInfo = getTerminalOutput(strCmd);
    qDebug()<<Q_FUNC_INFO<<__LINE__<<"tdappdesktop strinfo result : "<<strInfo;
    if (!strInfo.isEmpty()) {
        QStringList pidList;
        pidList = strInfo.split('\n');
        for (int i = 0; i < pidList.size(); i ++) {
            QString pidItem = pidList.at(i);
            if (pidItem.isEmpty()) {
                continue;
            }
            addProcDbus(pidItem.toInt());
            qDebug()<<Q_FUNC_INFO<<__LINE__<<"---- get Pid By Name and addProcDbus : "<<pidItem;
        }
    } else {
        qDebug() << Q_FUNC_INFO << __LINE__ << "get name " << strCmd << "is empty";
    }
    return true;
}

void ProxyServiceManager::onWindowAdded(WId id)
{
    KWindowInfo info(id, NET::WMName | NET::WMPid, NET::WM2AllProperties);
    qDebug()<<Q_FUNC_INFO<<"-------------"<<"the pid is :"<<info.pid()<<info.name();
    QString desktopName = confirmDesktopFile(info);
    QStringList appProxyList = getAppProxyFromFile();
    QStringList pidList = getProcAllPid(QString::number(info.pid()));
//    pidList.append(QString::number(info.pid()));
    if (appProxyList.contains(desktopName) && getProxyState() == true) {
        if (info.name().contains("腾讯文档"))
        {
            getPidByName();
        }
        qDebug()<<Q_FUNC_INFO<<__LINE__<<"the ppid is :"<<info.pid()<<info.name();
        for (int i = 0; i < pidList.size(); i ++) {
            qDebug()<<Q_FUNC_INFO<<__LINE__<<"all pid "<<i<<" is :" << pidList.at(i);
            QString pidItem = pidList.at(i);
            addProcDbus(pidItem.toInt());
        }
    }
    return;
}

QString ProxyServiceManager::searchFromEnviron(KWindowInfo info, QFileInfoList list)
{
    QFile file("/proc/" + QString::number(info.pid()) + "/environ");
    file.open(QIODevice::ReadOnly);
    QByteArray BA = file.readAll();
    file.close();
    QList<QByteArray> list_BA = BA.split('\0');

    QString desktopFilePath = nullptr;
    for (int i = 0; i < list_BA.length(); i++) {
        if (list_BA.at(i).startsWith("GIO_LAUNCHED_DESKTOP_FILE=")) {
            desktopFilePath = list_BA.at(i);
            desktopFilePath = desktopFilePath.mid(desktopFilePath.indexOf("=") + 1);
            //desktop文件地址需要重写
            desktopFilePath = desktopFilePath.mid(desktopFilePath.lastIndexOf("/") + 1);
            break;
        }
    }
    //desktop文件地址重写
    if (!desktopFilePath.isEmpty()) {
        for (int i = 0; i < list.size(); i++) {
            QFileInfo fileInfo = list.at(i);
            if (fileInfo.filePath() == DESKTOP_FILE_PATH + desktopFilePath) {
                desktopFilePath = fileInfo.filePath();
                return desktopFilePath;
            }
        }
    }
    return desktopFilePath;
}

QString ProxyServiceManager::searchAndroidApp(KWindowInfo info)
{
    QDir androidDir = QString(QDir::homePath() + ANDROID_FILE_PATH);
    QFileInfoList androidList = androidDir.entryInfoList();
    androidList.removeAll(QDir::homePath() + ANDROID_APP_CURRENT);
    androidList.removeAll(QDir::homePath() + ANDROID_APP_UPER);

    QFile file(QString("/proc/%1/cmdline").arg(info.pid()));
    file.open(QIODevice::ReadOnly);
    QByteArray cmd = file.readAll();
    file.close();
    QList<QByteArray> cmdList = cmd.split('\0');
    for (int i = 0; i < androidList.size(); i++) {
        QFileInfo fileInfo = androidList.at(i);
        QString desktopName = fileInfo.filePath();
        if (!fileInfo.filePath().endsWith(".desktop")) {
            continue;
        }
        desktopName = desktopName.mid(desktopName.lastIndexOf("/") + 1);
        desktopName = desktopName.left(desktopName.lastIndexOf("."));
        if(desktopName == cmdList.at(10)){
            return fileInfo.filePath();
        }
    }
    return nullptr;
}

QString ProxyServiceManager::compareClassName(QFileInfoList list)
{
    for (int i = 0; i < list.size(); i++) {
        QFileInfo fileInfo = list.at(i);;
        QString pathDesktopName = fileInfo.filePath();
        if (!fileInfo.filePath().endsWith(".desktop")) {
            continue;
        }
        pathDesktopName = pathDesktopName.mid(pathDesktopName.lastIndexOf("/") + 1);
        pathDesktopName = pathDesktopName.left(pathDesktopName.lastIndexOf("."));
        if (pathDesktopName == m_classClass || pathDesktopName == m_className || pathDesktopName == m_statusName)  {
            return fileInfo.filePath();
        }
    }
    return nullptr;
}

QString ProxyServiceManager::getDesktopFileName(QString cmd)
{
    char name[200];
    FILE *fp1 = NULL;
    if ((fp1 = popen(cmd.toStdString().data(), "r")) == NULL) {
        return QString();
    }
    memset(name, 0, sizeof(name));
    fgets(name, sizeof(name), fp1);
    pclose(fp1);
    return QString(name);
}

QString ProxyServiceManager::compareCmdExec(QFileInfoList list)
{
    for (int i = 0; i < list.size(); i++) {
        QString cmd;
        QFileInfo fileInfo = list.at(i);
        if (!fileInfo.filePath().endsWith(".desktop")) {
            continue;
        }
        cmd.sprintf(GET_DESKTOP_EXEC_NAME_MAIN, fileInfo.filePath().toStdString().data());
        QString desktopFileExeName = getDesktopFileName(cmd).remove("\n");

        if (desktopFileExeName.isEmpty()) {
            continue;
        }

        if (desktopFileExeName == m_cmdLine || desktopFileExeName.startsWith(m_cmdLine) || m_cmdLine.startsWith(desktopFileExeName)) {
            return fileInfo.filePath();
        }

        //仅仅是为了适配微信
        desktopFileExeName = "/usr/lib/" + desktopFileExeName;
        if (desktopFileExeName == m_cmdLine || desktopFileExeName.startsWith(m_cmdLine) || m_cmdLine.startsWith(desktopFileExeName)) {
            return fileInfo.filePath();
        }
    }
    return nullptr;
}

QString ProxyServiceManager::compareCmdName(QFileInfoList list)
{
    for (int i = 0; i < list.size(); i++) {
        QString cmd;
        QFileInfo fileInfo = list.at(i);
        if (!fileInfo.filePath().endsWith(".desktop")) {
            continue;
        }
        cmd.sprintf(GET_DESKTOP_EXEC_NAME_MAIN, fileInfo.filePath().toStdString().data());
        QString desktopFileExeName = getDesktopFileName(cmd).remove("\n");

        if (desktopFileExeName.isEmpty()) {
            continue;
        }

        if (desktopFileExeName.startsWith(m_className) || desktopFileExeName.endsWith(m_className)) {
            return fileInfo.filePath();
        }
    }
    return nullptr;
}

QString ProxyServiceManager::compareDesktopClass(QFileInfoList list)
{
    for (int i = 0; i < list.size(); i++) {
        QFileInfo fileInfo = list.at(i);
        QString pathDesktopName = fileInfo.filePath();
        if (!fileInfo.filePath().endsWith(".desktop")) {
            continue;
        }
        pathDesktopName = pathDesktopName.mid(pathDesktopName.lastIndexOf("/") + 1);
        pathDesktopName = pathDesktopName.left(pathDesktopName.lastIndexOf("."));

        if (pathDesktopName.startsWith(m_className) || pathDesktopName.endsWith(m_className)) {
            return fileInfo.filePath();
        }
        else if (m_className.startsWith(pathDesktopName) || m_className.endsWith(pathDesktopName)) {
            return fileInfo.filePath();
        }
    }
    return nullptr;
}

QString ProxyServiceManager::containsName(QFileInfoList list)
{
    for (int i = 0; i < list.size(); i++) {
        QString cmd;
        QFileInfo fileInfo = list.at(i);
        QString pathDesktopName = fileInfo.filePath();

        if (!fileInfo.filePath().endsWith(".desktop")) {
            continue;
        }

        cmd.sprintf(GET_DESKTOP_EXEC_NAME_MAIN, fileInfo.filePath().toStdString().data());
        QString desktopFileExeName = getDesktopFileName(cmd).remove("\n");

        pathDesktopName = pathDesktopName.mid(pathDesktopName.lastIndexOf("/") + 1);
        pathDesktopName = pathDesktopName.left(pathDesktopName.lastIndexOf("."));

        if (pathDesktopName.contains(m_className) || desktopFileExeName.contains(m_className)) {
            return fileInfo.filePath();
        }
    }
    return nullptr;
}

QString ProxyServiceManager::compareLastStrategy(QFileInfoList list)
{
    QString desktopFilePath = compareCmdName(list);

    if (desktopFilePath.isEmpty()) {
        desktopFilePath = compareDesktopClass(list);
    }

    if (desktopFilePath.isEmpty()) {
        desktopFilePath = containsName(list);
    }
    return desktopFilePath;
}

QString ProxyServiceManager::confirmDesktopFile(KWindowInfo info)
{
    QString desktopFilePath = nullptr;
    QDir dir = QDir(DESKTOP_FILE_PATH);
    QFileInfoList list = dir.entryInfoList();
    //跳过 ./ 和 ../ 目录
    list.removeAll(QFile(USR_SHARE_APP_CURRENT));
    list.removeAll(QFile(USR_SHARE_APP_UPER));

    //第一种方法：获取点击应用时大部分desktop文件名
    desktopFilePath = searchFromEnviron(info, list);

    //第二种方法：比较名字一致性
    if (desktopFilePath.isEmpty()) {
        m_classClass = info.windowClassClass().toLower();
        m_className = info.windowClassName();

        //匹配安卓兼容
        if (m_className == "kylin-kmre-window") {
            return searchAndroidApp(info);
        }

        QFile file(QString("/proc/%1/status").arg(info.pid()));
        if (file.open(QIODevice::ReadOnly)) {
            char buf[1024];
            qint64 len=file.readLine(buf,sizeof(buf));
            if (len!=-1) {
                m_statusName = QString::fromLocal8Bit(buf).remove("Name:").remove("\t").remove("\n");
            }
        }
        desktopFilePath = compareClassName(list);
    }

    //第三种方法：比较cmd命令行操作一致性
    if (desktopFilePath.isEmpty()) {
        QFile file(QString("/proc/%1/cmdline").arg(info.pid()));
        if (file.open(QIODevice::ReadOnly)) {
            char buf[1024];
            qint64 len=file.readLine(buf,sizeof(buf));
            if (len!=-1) {
                m_cmdLine = QString::fromLocal8Bit(buf).remove("\n");
            }
        }
        desktopFilePath = compareCmdExec(list);
    }

    //第四种方法：匹配部分字段
    if (desktopFilePath.isEmpty()) {
        desktopFilePath = compareLastStrategy(list);
    }
    return desktopFilePath;
}

void ProxyServiceManager::wirteJsonFile(QString filePath, const QJsonObject obj)
{
    if (filePath.isEmpty() || obj.isEmpty()) {
        qDebug()<<Q_FUNC_INFO<<__LINE__<<"function input filePath or obj is error!";
        return;
    }
    QFile file(filePath);
    file.open(QIODevice::WriteOnly | QIODevice::Truncate);
    QJsonDocument writeDoc = QJsonDocument(obj);
    file.write(writeDoc.toJson());
    qDebug()<<Q_FUNC_INFO<<__LINE__<<filePath<<"write success!";
    file.flush();
    file.close();
}

void ProxyServiceManager::addProcDbus(const qint32 pid)
{
    if (!m_proxyInterface->isValid()) {
        qDebug()<<Q_FUNC_INFO<<__LINE__<<"m_proxyInterface dbus is not valid!";
        return;
    }

    m_proxyInterface->call("AddProc", pid);
}

void ProxyServiceManager::startProxyDbus(const QJsonObject obj)
{
    if (obj.isEmpty()) {
        qDebug()<<Q_FUNC_INFO<<__LINE__<<"obj is error!";
        return;
    }

    if (!m_proxyInterface->isValid()) {
        qDebug()<<Q_FUNC_INFO<<__LINE__<<"m_proxyInterface dbus is not valid!";
        return;
    }

    QString proto = obj.value(PROTOJSON_KEY_TYPE).toString();
    m_proxyInterface->call("StartProxy", proto, "default", false);
    qDebug()<<Q_FUNC_INFO<<__LINE__<<"StartProxy"<<proto<<"default"<<"false";
}

void ProxyServiceManager::addProxyDbus(const QJsonObject obj)
{
    if (obj.isEmpty()) {
        qDebug()<<Q_FUNC_INFO<<__LINE__<<"obj is error!";
        return;
    }

    if (!m_proxyInterface->isValid()) {
        qDebug()<<Q_FUNC_INFO<<__LINE__<<"m_proxyInterface dbus is not valid!";
        return;
    }

    QJsonObject confObj = obj;
    confObj.remove(PROTOJSON_KEY_STATE);

    QString proto = confObj.value(PROTOJSON_KEY_TYPE).toString();
    QByteArray objArray = QJsonDocument(confObj).toJson();
    m_proxyInterface->call("AddProxy", proto, "default", objArray);
    qDebug()<<Q_FUNC_INFO<<__LINE__<<proto<<"default"<<objArray;
}

void ProxyServiceManager::clearProxyDbus()
{
    if (!m_proxyInterface->isValid()) {
        qDebug()<<Q_FUNC_INFO<<__LINE__<<"m_proxyInterface dbus is not valid!";
        return;
    }

    m_proxyInterface->call("ClearProxy");
    qDebug()<<Q_FUNC_INFO<<__LINE__;
}

void ProxyServiceManager::startProxy(const QJsonObject obj)
{
    clearProxyDbus();
    addProxyDbus(obj);
    startProxyDbus(obj);
    setProxyState(true);
}

void ProxyServiceManager::stopProxyDbus()
{
    if (!m_proxyInterface->isValid()) {
        qDebug()<<Q_FUNC_INFO<<__LINE__<<"m_proxyInterface dbus is not valid!";
        return;
    }

    m_proxyInterface->call("StopProxy");
    qDebug()<<Q_FUNC_INFO<<__LINE__;
}

void ProxyServiceManager::stopProxy()
{
    stopProxyDbus();
    QString jsonPath = QDir::homePath() + "/" + PROXYCONF_FILE;
    QJsonObject protoObj = readJsonFile(jsonPath);
    protoObj.insert(PROTOJSON_KEY_STATE, QJsonValue(false));
    wirteJsonFile(jsonPath, protoObj);
    setProxyState(false);
}

QJsonObject ProxyServiceManager::dealJsonObj(const QStringList configList)
{
    QJsonObject configObj = QJsonObject();
    if (configList.isEmpty() && configList.count() < 3) {
        qDebug()<<Q_FUNC_INFO<<__LINE__<<"configList item less!";
        return configObj;
    }

    configObj.insert(PROTOJSON_KEY_TYPE, QJsonValue(configList.at(0)));
    configObj.insert(PROTOJSON_KEY_NAME, QJsonValue("default"));
    configObj.insert(PROTOJSON_KEY_SERVER, QJsonValue(configList.at(1)));
    QString prot = configList.at(2);
    configObj.insert(PROTOJSON_KEY_PORT, QJsonValue(prot.toInt()));
    switch (configList.count()) {
    case 4:
        configObj.insert(PROTOJSON_KEY_USRNAME, QJsonValue(configList.at(3)));
        configObj.insert(PROTOJSON_KEY_PASSWORD, QJsonValue(""));
        break;

    case 5:
        configObj.insert(PROTOJSON_KEY_USRNAME, QJsonValue(configList.at(3)));
        configObj.insert(PROTOJSON_KEY_PASSWORD, QJsonValue(configList.at(4)));
        break;

    default:
        configObj.insert(PROTOJSON_KEY_USRNAME, QJsonValue(""));
        configObj.insert(PROTOJSON_KEY_PASSWORD, QJsonValue(""));
        break;
    }
    configObj.insert(PROTOJSON_KEY_STATE, QJsonValue(true));
    return configObj;
}

QStringList ProxyServiceManager::getProxyConfig()
{
    QStringList proxyConfList;
    proxyConfList.clear();

    QString jsonPath = QDir::homePath() + "/" + PROXYCONF_FILE;
    QJsonObject readObj = readJsonFile(jsonPath);
    proxyConfList.append(readObj.value(PROTOJSON_KEY_TYPE).toString());
    proxyConfList.append(readObj.value(PROTOJSON_KEY_SERVER).toString());
    QString portStr = QString::number(readObj.value(PROTOJSON_KEY_PORT).toInt());
    proxyConfList.append(portStr);
    proxyConfList.append(readObj.value(PROTOJSON_KEY_USRNAME).toString());
    proxyConfList.append(readObj.value(PROTOJSON_KEY_PASSWORD).toString());

    return proxyConfList;
}

void ProxyServiceManager::setProxyConfig(const QStringList configList)
{
    QString jsonPath = QDir::homePath() + "/" + PROXYCONF_FILE;
    QJsonObject readObj = readJsonFile(jsonPath);
    QJsonObject configObj = dealJsonObj(configList);
    startProxy(configObj);
    if (configObj != readObj) {
        wirteJsonFile(jsonPath, configObj);
    }
}

void ProxyServiceManager::delValueFromArray(QJsonArray *array, const QJsonValue item)
{
    if (array != nullptr && !array->isEmpty()) {
        for (int i = 0; i < array->count(); i++) {
            if (array->at(i) == item) {
                array->removeAt(i);
                return;
            }
        }
    }
}

QJsonObject ProxyServiceManager::readJsonFile(QString filePath)
{
    QJsonObject readObj = QJsonObject();
    if (filePath.isEmpty() || !QFile(filePath).exists()) {
        qDebug()<<Q_FUNC_INFO<<__LINE__<<filePath<<"is not exits!";
        return readObj;
    }

    QFile file(filePath);
    file.open(QIODevice::ReadOnly);
    QByteArray readBy = file.readAll();
    file.close();
    QJsonParseError error;
    QJsonDocument readDoc = QJsonDocument::fromJson(readBy, &error);
    if (!readDoc.isEmpty() && error.error == QJsonParseError::NoError) {
        readObj = readDoc.object();
        qDebug()<<Q_FUNC_INFO<<__LINE__<<filePath<<"read success!";
    }

    return readObj;
}

void ProxyServiceManager::setProxyFile(QString desktopfp, bool create)
{
    QString jsonPath = QDir::homePath() + "/" + APPPROXY_FILE;
    QJsonObject readObj = readJsonFile(jsonPath);
    QJsonObject writeObj = readObj;

    if (readObj.isEmpty()) {
        if (create) {
            QJsonArray appArray = QJsonArray();
            appArray.append(QJsonValue(desktopfp));
            writeObj.insert(JSON_KEY_APPLICATION, appArray);
            qDebug()<<Q_FUNC_INFO<<__LINE__<<desktopfp<<" add to proxy list";
        } else {
            qDebug()<<Q_FUNC_INFO<<__LINE__<<jsonPath<<"is error!";
        }
    } else {
        QJsonArray appArray = readObj.value(JSON_KEY_APPLICATION).toArray();
        QJsonValue arrayItem = QJsonValue(desktopfp);
        if (create && !appArray.contains(arrayItem)) {
            appArray.append(arrayItem);
            qDebug()<<Q_FUNC_INFO<<__LINE__<<desktopfp<<" add to proxy list";
        }
        if (!create && appArray.contains(arrayItem)) {
            qDebug()<<Q_FUNC_INFO<<__LINE__<<desktopfp<<" remove from proxy list";
            delValueFromArray(&appArray, arrayItem);
        }
        writeObj.insert(JSON_KEY_APPLICATION, appArray);
    }

    if (writeObj != readObj) {
        wirteJsonFile(jsonPath, writeObj);
    }
}

void ProxyServiceManager::addAppIntoProxy(QString desktopfp)
{
    if (desktopfp.isEmpty()) {
        qDebug()<<Q_FUNC_INFO<<__LINE__<<"desktopfp is Empty!";
        return;
    }

    setProxyFile(desktopfp, true);
}

void ProxyServiceManager::delAppIntoProxy(QString desktopfp)
{
    if (desktopfp.isEmpty()) {
        qDebug()<<Q_FUNC_INFO<<__LINE__<<"desktopfp is Empty!";
        return;
    }

    setProxyFile(desktopfp, false);
}

void ProxyServiceManager::setProxyStateDbus(bool state)
{
    if (state) {
        QString jsonPath = QDir::homePath() + "/" + PROXYCONF_FILE;
        QJsonObject proxyObj = readJsonFile(jsonPath);
        proxyObj.insert(PROTOJSON_KEY_STATE, QJsonValue(true));
        wirteJsonFile(jsonPath, proxyObj);
        startProxy(proxyObj);
    } else {
        stopProxy();
    }
}

bool ProxyServiceManager::getProxyStateDbus()
{
    QString jsonPath = QDir::homePath() + "/" + PROXYCONF_FILE;
    QJsonObject readObj = readJsonFile(jsonPath);
    return readObj.value(PROTOJSON_KEY_STATE).toBool();
}

QStringList ProxyServiceManager::getAppProxyFromFile()
{
    QStringList appProxyList;
    appProxyList.clear();
    QString jsonPath = QDir::homePath() + "/" + APPPROXY_FILE;
    QJsonObject readObj = readJsonFile(jsonPath);
    QJsonArray appArray = readObj.value("application").toArray();
    if (!appArray.isEmpty()) {
        for (auto appItem : appArray) {
            appProxyList.append(appItem.toString());
        }
    }
    return appProxyList;
}

QMap<QString, QStringList> ProxyServiceManager::getAppProxy()
{
    QMap<QString, QStringList> appMap = initAppInfoMap();
    QStringList appProxyList = getAppProxyFromFile();
    if (!appProxyList.isEmpty()) {
        for (auto iter = appMap.begin(); iter != appMap.end(); iter ++) {
            if (appProxyList.contains(iter.key())) {
                QStringList appInfo = iter.value();
                appInfo.replace(2, "true");
                appMap.insert(iter.key(), appInfo);
            }
        }
    }

    return appMap;
}

QMap<QString, QStringList> ProxyServiceManager::initAppInfoMap()
{
    QStringList desktopfpList = getDesktopFilePath();
    QStringList customAppList = getCustomizedAppList(CUSTOMAPP_FILE);
    QMap<QString, QStringList> appPathMap;
    appPathMap.clear();
    for (auto desktopfp : desktopfpList) {
        QString name = getAppName(desktopfp);

        //定制软件判断，若文档是空的，则认为没有开定制功能
        if (!customAppList.isEmpty() && !customAppList.contains(name)) {
            continue;
        }

        QStringList appInfoList;
        appInfoList.append(name);
        appInfoList.append(getAppIcon(desktopfp));
        appInfoList.append("false");
        appPathMap.insert(desktopfp, appInfoList);
    }
    return appPathMap;
}

//获取定制软件列表
QStringList ProxyServiceManager::getCustomizedAppList(QString filePath)
{
    QStringList customizedAppList;
    customizedAppList.clear();

    QJsonObject readObj = readJsonFile(filePath);
    QJsonArray appArray = readObj.value(JSON_KEY_APPLICATION).toArray();
    for (auto arrayItem : appArray) {
        customizedAppList.append(arrayItem.toString());
    }

    return customizedAppList;
}

//获取系统desktop文件路径
QStringList ProxyServiceManager::getDesktopFilePath()
{
    m_filePathList.clear();
    QString jsonPath = QDir::homePath() + "/.config/ukui-menu-security-config.json";
    QFile file(jsonPath);

    if (file.exists()) {
        file.open(QIODevice::ReadOnly);
        QByteArray readBy = file.readAll();
        QJsonParseError error;
        QJsonDocument readDoc = QJsonDocument::fromJson(readBy, &error);

        if (!readDoc.isNull() && error.error == QJsonParseError::NoError) {
            QJsonObject obj = readDoc.object().value("ukui-menu").toObject();

            if (obj.value("mode").toString() == "whitelist") {
                QJsonArray blArray = obj.value("whitelist").toArray();
                QJsonArray enArray = blArray.at(0).toObject().value("entries").toArray();

                for (int index = 0; index < enArray.size(); index++) {
                    QJsonObject obj = enArray.at(index).toObject();
                    m_filePathList.append(obj.value("path").toString());
                }

                return m_filePathList;
            } else if (obj.value("mode").toString() == "blacklist") {
                getAndroidApp();
                recursiveSearchFile("/usr/share/applications/");
                recursiveSearchFile("/var/lib/snapd/desktop/applications/");
                recursiveSearchFile("/var/lib/flatpak/exports/share/applications/");
                QJsonArray blArray = obj.value("blacklist").toArray();
                QJsonArray enArray = blArray.at(0).toObject().value("entries").toArray();

                for (int index = 0; index < enArray.size(); index++) {
                    QJsonObject obj = enArray.at(index).toObject();
                    m_filePathList.removeAll(obj.value("path").toString());
                }
            } else {
                getAndroidApp();
                recursiveSearchFile("/usr/share/applications/");
                recursiveSearchFile("/var/lib/snapd/desktop/applications/");
                recursiveSearchFile("/var/lib/flatpak/exports/share/applications/");
            }
        }

        file.close();
    } else {
        getAndroidApp();
        recursiveSearchFile("/usr/share/applications/");
        recursiveSearchFile("/var/lib/snapd/desktop/applications/");
        recursiveSearchFile("/var/lib/flatpak/exports/share/applications/");
    }

    m_filePathList.removeAll("/usr/share/applications/software-properties-livepatch.desktop");
    m_filePathList.removeAll("/usr/share/applications/mate-color-select.desktop");
    m_filePathList.removeAll("/usr/share/applications/blueman-adapters.desktop");
    m_filePathList.removeAll("/usr/share/applications/mate-user-guide.desktop");
    m_filePathList.removeAll("/usr/share/applications/nm-connection-editor.desktop");
    m_filePathList.removeAll("/usr/share/applications/debian-uxterm.desktop");
    m_filePathList.removeAll("/usr/share/applications/debian-xterm.desktop");
    m_filePathList.removeAll("/usr/share/applications/im-config.desktop");
    m_filePathList.removeAll("/usr/share/applications/fcitx.desktop");
    m_filePathList.removeAll("/usr/share/applications/fcitx-configtool.desktop");
    m_filePathList.removeAll("/usr/share/applications/onboard-settings.desktop");
    m_filePathList.removeAll("/usr/share/applications/info.desktop");
    m_filePathList.removeAll("/usr/share/applications/ukui-power-preferences.desktop");
    m_filePathList.removeAll("/usr/share/applications/ukui-power-statistics.desktop");
    m_filePathList.removeAll("/usr/share/applications/software-properties-drivers.desktop");
    m_filePathList.removeAll("/usr/share/applications/software-properties-gtk.desktop");
    m_filePathList.removeAll("/usr/share/applications/gnome-session-properties.desktop");
    m_filePathList.removeAll("/usr/share/applications/org.gnome.font-viewer.desktop");
    m_filePathList.removeAll("/usr/share/applications/xdiagnose.desktop");
    m_filePathList.removeAll("/usr/share/applications/gnome-language-selector.desktop");
    m_filePathList.removeAll("/usr/share/applications/mate-notification-properties.desktop");
    m_filePathList.removeAll("/usr/share/applications/transmission-gtk.desktop");
    m_filePathList.removeAll("/usr/share/applications/mpv.desktop");
    m_filePathList.removeAll("/usr/share/applications/system-config-printer.desktop");
    m_filePathList.removeAll("/usr/share/applications/org.gnome.DejaDup.desktop");
    m_filePathList.removeAll("/usr/share/applications/yelp.desktop");
    //v10
    m_filePathList.removeAll("/usr/share/applications/mate-about.desktop");
    m_filePathList.removeAll("/usr/share/applications/time.desktop");
    m_filePathList.removeAll("/usr/share/applications/network.desktop");
    m_filePathList.removeAll("/usr/share/applications/shares.desktop");
    m_filePathList.removeAll("/usr/share/applications/mate-power-statistics.desktop");
    m_filePathList.removeAll("/usr/share/applications/display-im6.desktop");
    m_filePathList.removeAll("/usr/share/applications/display-im6.q16.desktop");
    m_filePathList.removeAll("/usr/share/applications/openjdk-8-policytool.desktop");
    m_filePathList.removeAll("/usr/share/applications/kylin-io-monitor.desktop");
    m_filePathList.removeAll("/usr/share/applications/wps-office-uninstall.desktop");
    m_filePathList.removeAll("/usr/share/applications/wps-office-misc.desktop");
//    m_filePathList.removeAll("/usr/share/applications/kylin-installer.desktop");
    QStringList desktopList;

    for (int i = 0; i < m_filePathList.count(); ++i) {
        QString filepath = m_filePathList.at(i);
        int list_index = filepath.lastIndexOf('/');
        QString desktopName = filepath.right(filepath.length() - list_index - 1);

        if (desktopList.contains(desktopName)) {
            m_filePathList.removeAll(filepath);
            i--;
        } else {
            desktopList.append(desktopName);
        }
    }

    return m_filePathList;
}

QString ProxyServiceManager::getAppName(QString desktopfp)
{
    GError **error = nullptr;
    GKeyFileFlags flags = G_KEY_FILE_NONE;
    GKeyFile *keyfile = g_key_file_new();
    QByteArray fpbyte = desktopfp.toLocal8Bit();
    char *filepath = fpbyte.data();
    g_key_file_load_from_file(keyfile, filepath, flags, error);
    char *name = g_key_file_get_locale_string(keyfile, "Desktop Entry", "Name", nullptr, nullptr);
    QString namestr = QString::fromLocal8Bit(name);
    g_key_file_free(keyfile);
    return namestr;
}

//获取应用图标
QString ProxyServiceManager::getAppIcon(QString desktopfp)
{
    GError **error = nullptr;
    GKeyFileFlags flags = G_KEY_FILE_NONE;
    GKeyFile *keyfile = g_key_file_new();
    QByteArray fpbyte = desktopfp.toLocal8Bit();
    char *filepath = fpbyte.data();
    g_key_file_load_from_file(keyfile, filepath, flags, error);
    char *icon = g_key_file_get_locale_string(keyfile, "Desktop Entry", "Icon", nullptr, nullptr);
    g_key_file_free(keyfile);
    return QString::fromLocal8Bit(icon);
}

//文件递归查询
void ProxyServiceManager::recursiveSearchFile(const QString &_filePath)
{
    QDir dir(_filePath);

    if (!dir.exists()) {
        return;
    }

    dir.setFilter(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot);
    dir.setSorting(QDir::DirsFirst);
    QFileInfoList list = dir.entryInfoList();
    list.removeAll(QFileInfo("/usr/share/applications/screensavers"));

    if (list.size() < 1) {
        return;
    }

    int i = 0;

    //递归算法的核心部分
    do {
        QFileInfo fileInfo = list.at(i);
        //如果是文件夹，递归
        bool isDir = fileInfo.isDir();

        if (isDir) {
            recursiveSearchFile(fileInfo.filePath());
        } else {
            //过滤后缀不是.desktop的文件
            QString filePathStr = fileInfo.filePath();

            if (!filePathStr.endsWith(".desktop")) {
                i++;
                continue;
            }

            QByteArray fpbyte = filePathStr.toLocal8Bit();
            char *filepath = fpbyte.data();

            if (0 != access(filepath, R_OK)) { //判断文件是否可读
                i++;
                continue;
            }

            m_keyfile = g_key_file_new();

            if (!g_key_file_load_from_file(m_keyfile, filepath, m_flags, m_error)) {
                return;
            }

            char *ret_0 = g_key_file_get_locale_string(m_keyfile, "Desktop Entry", "Categories", nullptr, nullptr);

            if (ret_0 != nullptr) {
                QString str = QString::fromLocal8Bit(ret_0);

                if (str.contains("Android")) {
                    g_key_file_free(m_keyfile);
                    i++;
                    continue;
                }
            }

            char *ret_1 = g_key_file_get_locale_string(m_keyfile, "Desktop Entry", "NoDisplay", nullptr, nullptr);

            if (ret_1 != nullptr) {
                QString str = QString::fromLocal8Bit(ret_1);

                if (str.contains("true")) {
                    g_key_file_free(m_keyfile);
                    i++;
                    continue;
                }
            }

            char *ret_2 = g_key_file_get_locale_string(m_keyfile, "Desktop Entry", "NotShowIn", nullptr, nullptr);

            if (ret_2 != nullptr) {
                QString str = QString::fromLocal8Bit(ret_2);

                if (str.contains("UKUI")) {
                    g_key_file_free(m_keyfile);
                    i++;
                    continue;
                }
            }

            //过滤LXQt、KDE
            char *ret = g_key_file_get_locale_string(m_keyfile, "Desktop Entry", "OnlyShowIn", nullptr, nullptr);

            if (ret != nullptr) {
                QString str = QString::fromLocal8Bit(ret);

                if (str.contains("LXQt") || str.contains("KDE")) {
                    g_key_file_free(m_keyfile);
                    i++;
                    continue;
                }
            }

            g_key_file_free(m_keyfile);
            m_filePathList.append(filePathStr);
        }

        i++;
    } while (i < list.size());
}

void ProxyServiceManager::getAndroidApp()
{
    m_androidDesktopfnList.clear();
    QVector<QStringList> androidVector;
    androidVector.clear();
    QString path = QDir::homePath() + "/.local/share/applications/";
    QDir dir(path);

    if (!dir.exists()) {
        return;
    }

    dir.setFilter(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot);
    dir.setSorting(QDir::DirsFirst);
    QFileInfoList list = dir.entryInfoList();

    if (list.size() < 1) {
        return;
    }

    int i = 0;
    GKeyFile *keyfile = g_key_file_new();

    do {
        QFileInfo fileInfo = list.at(i);

        if (!fileInfo.isFile()) {
            i++;
            continue;
        }

        //过滤后缀不是.desktop的文件
        QString filePathStr = fileInfo.filePath();

        if (!filePathStr.endsWith(".desktop")) {
            i++;
            continue;
        }

        QByteArray fpbyte = filePathStr.toLocal8Bit();
        char *filepath = fpbyte.data();

        if (0 != access(filepath, R_OK)) { //判断文件是否可读
            i++;
            continue;
        }

        m_keyfile = g_key_file_new();

        if (!g_key_file_load_from_file(m_keyfile, filepath, m_flags, m_error)) {
            return;
        }

        char *ret_1 = g_key_file_get_locale_string(m_keyfile, "Desktop Entry", "NoDisplay", nullptr, nullptr);

        if (ret_1 != nullptr) {
            QString str = QString::fromLocal8Bit(ret_1);

            if (str.contains("true")) {
                g_key_file_free(m_keyfile);
                i++;
                continue;
            }
        }

        char *ret_2 = g_key_file_get_locale_string(m_keyfile, "Desktop Entry", "NotShowIn", nullptr, nullptr);

        if (ret_2 != nullptr) {
            QString str = QString::fromLocal8Bit(ret_2);

            if (str.contains("UKUI")) {
                g_key_file_free(m_keyfile);
                i++;
                continue;
            }
        }

        //过滤LXQt、KDE
        char *ret = g_key_file_get_locale_string(m_keyfile, "Desktop Entry", "OnlyShowIn", nullptr, nullptr);

        if (ret != nullptr) {
            QString str = QString::fromLocal8Bit(ret);

            if (str.contains("LXQt") || str.contains("KDE")) {
                g_key_file_free(m_keyfile);
                i++;
                continue;
            }
        }

        m_filePathList.append(filePathStr);
        m_androidDesktopfnList.append(fileInfo.fileName());
        i++;
    } while (i < list.size());

    g_key_file_free(keyfile);
}
